﻿// See https://aka.ms/new-console-template for more information
//生成器模式
//假如有这么一个复杂对象，在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。
//这些初始化代码通常隐藏与一个包含众多参数让人基本看不懂的构造函数中，甚至还有更糟的情况，这些代码散落在客户端的多个位置
//例如：我们创建一个房屋对象，首先我们需要四面墙和地板，安装房门和一套窗户，然后再建造一个屋顶，但是如果我们想要一栋更宽敞明亮的房屋，还要有院子和其他设置，该怎么办呢？
//最简单的方法是扩展房屋基类，然后创建一系列涵盖所有参数的子类，但是最终你会面对相当数量的子类，任何新增都会让这个层次结构更为复杂
//或者你在房屋基类中创建一个包括所有可能的参数的超级构造函数，这可以避免生成子类，但是却会使得构造函数变得十分不简洁，因为通常只有很少的房屋有泳池，因此泳池相关的参数是毫无用处的

//解决方案：生成器模式建议将对象构造代码从产品类中抽取出来， 并将其放在一个名为生成器的独立对象中。
//主管:你可以进一步将用于创建产品的一系列生成器步骤调用抽取成为单独的主管类。 主管类可定义创建步骤的执行顺序， 而生成器则提供这些步骤的实现。
//当产品较为复杂且需要详细配置时，使用生成器模式才有意义

//汽车生成器
CarBuilder carBuilder = new CarBuilder();
//主管
Director director = new Director();
//主管装配汽车
director.ConstructsuvCar(carBuilder);
//获取产品
var result = carBuilder.GetProduct();

Console.WriteLine(result.Content);

//一辆汽车可能配备有GPS，行车电脑和几个座位，不同型号的汽车会配有不同的功能
internal class Car
{
    /// <summary>
    /// 构成
    /// </summary>
    public string Content { get; set; }
}

//用户使用手册应该根据汽车配置进行编制，并介绍汽车的所有功能
internal class Manual
{
    /// <summary>
    /// 构成
    /// </summary>
    public string Content { get; set; }
}

internal interface IBuilder
{
    void Reset();//重置

    void SetSeats();//设置座位

    void SetEngine();//设置引擎

    void SetTripComputer();//设置车载电脑

    void SetGPS();//设置GPS
}

//生成器模式可以和工厂模式结合使用，生成器的产品的零部件，可以由工厂构造提供
internal class CarBuilder : IBuilder
{
    //新的生成器实例必须包含一个在后续组装过程中使用的空产品对象
    private Car _car;

    public CarBuilder()
    {
        this.Reset();
    }

    //重置方法可以清除正在生成的对象。
    public void Reset()
    {
        this._car = new Car();
    }

    //所有生成步骤都会与同一个产品实例进行交互

    //设置座位
    public void SetSeats()
    {
        this._car.Content += "安装座位；";
    }

    //设置引擎
    public void SetEngine()
    {
        this._car.Content += "安装引擎；";
    }

    //设置suv汽车引擎
    public void SetSuvEngine()
    {
        this._car.Content += "安装suv汽车引擎;";
    }

    //设置跑车引擎
    public void SetSportsEngine()
    {
        this._car.Content += "设置跑车引擎;";
    }

    //安装车载电脑
    public void SetTripComputer()
    {
        this._car.Content += "安装车载电脑；";
    }

    //安装gps
    public void SetGPS()
    {
        this._car.Content += "安装GPS；";
    }

    // 具体生成器需要自行提供获取结果的方法。这是因为不同类型的生成器可能
    // 会创建不遵循相同接口的、完全不同的产品。所以也就无法在生成器接口中
    // 声明这些方法（至少在静态类型的编程语言中是这样的）。
    //
    // 通常在生成器实例将结果返回给客户端后，它们应该做好生成另一个产品的
    // 准备。因此生成器实例通常会在 `getProduct（获取产品）`方法主体末尾
    // 调用重置方法。但是该行为并不是必需的，你也可让生成器等待客户端明确
    // 调用重置方法后再去处理之前的结果。
    public Car GetProduct()
    {
        var product = this._car;
        this.Reset();
        return product;
    }
}

//生成器与其他创建型模式的不同之处在于，他能让你创建不遵顼相同接口的产品。
internal class CarManualBuilder : IBuilder
{
    private Manual _manual;

    public CarManualBuilder()
    {
        this.Reset();
    }

    public void Reset()
    {
        this._manual = new Manual();
    }

    //添加关于汽车座椅功能的文档
    public void SetSeats()
    {
        this._manual.Content += "添加座椅功能文档";
    }

    //添加关于引擎的介绍
    public void SetEngine()
    {
        this._manual.Content += "添加引擎介绍";
    }

    //添加关于车载电脑的介绍
    public void SetTripComputer()
    {
        this._manual.Content += "添加车载电脑功能介绍";
    }

    //添加关于GPS的介绍
    public void SetGPS()
    {
        this._manual.Content += "添加GPS功能文档";
    }

    //返回使用收车并重置产品
    public Manual GetProduct()
    {
        var obj = this._manual;
        this.Reset();
        return obj;
    }
}

//主管只负责按照特定顺序执行生成步骤，其在根据特定步骤或配置来生成产品时会恒有帮助，由于客户端可以直接控制生成器，所以严格意义上来说，主管类并不是必须的
internal class Director
{
    // 主管可同由客户端代码传递给自身的任何生成器实例进行交互。客户端可通
    // 过这种方式改变最新组装完毕的产品的最终类型。主管可使用同样的生成步
    // 骤创建多个产品变体。

    //组装suvcar
    public void ConstructsuvCar(CarBuilder carBuilder)
    {
        carBuilder.Reset();
        carBuilder.SetSeats();
        carBuilder.SetSuvEngine();//安装suv汽车引擎
        carBuilder.SetTripComputer();
        carBuilder.SetGPS();
    }

    //组装sportscar
    public void ConstructSportsCar(CarBuilder carBuilder)
    {
        carBuilder.Reset();
        carBuilder.SetSeats();
        carBuilder.SetSportsEngine();//安装Sports汽车引擎
        carBuilder.SetTripComputer();
        carBuilder.SetGPS();
    }
}